<template>
  <div ref="popperContainerNode" class="ad-tooltip" v-on="outerEvents">
    <div ref="triggerNode" class="vk-tooltip__trigger" v-on="events">
      <slot />
    </div>
    <Transition :name="transition">
      <div v-if="isOpen" ref="popperNode" class="ad-tooltip__popper">
        <slot name="content">
          {{ content }}
        </slot>
        <div id="arrow" data-popper-arrow></div>
      </div>
    </Transition>
  </div>
</template>
<script setup lang="ts">
import { ref, watch, reactive, onUnmounted, computed } from 'vue';
import { createPopper } from '@popperjs/core';
import type { Instance } from '@popperjs/core';
import { debounce } from 'lodash-es';
import useClickOutside from './../hooks/useClickOutside';

import type { Placement, Options } from '@popperjs/core';

interface TooltipProps {
  content?: string;
  trigger?: 'hover' | 'click';
  placement?: Placement;
  manual?: boolean;
  popperOptions?: Partial<Options>;
  transition?: string;
  openDelay?: number;
  closeDelay?: number;
}

interface TooltipEmits {
  (e: 'visible-change', value: boolean): void;
  (e: 'click-outside', value: boolean): void;
}

interface TooltipInstance {
  show: () => void;
  hide: () => void;
}

const props = withDefaults(defineProps<TooltipProps>(), {
  placement: 'bottom',
  trigger: 'hover',
  transition: 'fade',
  openDelay: 0,
  closeDelay: 0,
});
const emits = defineEmits<TooltipEmits>();
const isOpen = ref(false);
const popperNode = ref<HTMLElement>();
const triggerNode = ref<HTMLElement>();
const popperContainerNode = ref<HTMLElement>();
let popperInstance: null | Instance = null;
let events: Record<string, any> = reactive({});
let outerEvents: Record<string, any> = reactive({});
let openTimes = 0;
let closeTimes = 0;
const popperOptions = computed(() => {
  return {
    placement: props.placement,
    modifiers: [
      {
        name: 'offset',
        options: {
          offset: [0, 9],
        },
      },
    ],
    ...props.popperOptions,
  };
});

const open = () => {
  openTimes++;
  console.log('open times', openTimes);
  isOpen.value = true;
  emits('visible-change', true);
};
const close = () => {
  closeTimes++;
  console.log('close times', closeTimes);
  isOpen.value = false;
  emits('visible-change', false);
};
const openDebounce = debounce(open, props.openDelay);
const closeDebounce = debounce(close, props.closeDelay);

const openFinal = () => {
  closeDebounce.cancel();
  openDebounce();
};
const closeFinal = () => {
  openDebounce.cancel();
  closeDebounce();
};

const togglePopper = () => {
  if (isOpen.value) {
    closeFinal();
  } else {
    openFinal();
  }
};
useClickOutside(popperContainerNode, () => {
  if (props.trigger === 'click' && isOpen.value && !props.manual) {
    closeFinal();
  }
  if (isOpen.value) {
    emits('click-outside', true);
  }
});
const attachEvents = () => {
  if (props.trigger === 'hover') {
    events['mouseenter'] = openFinal;
    outerEvents['mouseleave'] = closeFinal;
  } else if (props.trigger === 'click') {
    events['click'] = togglePopper;
  }
};
if (!props.manual) {
  attachEvents();
}
watch(
  () => props.manual,
  (isManual) => {
    if (isManual) {
      events = {};
      outerEvents = {};
    } else {
      attachEvents();
    }
  },
);
watch(
  () => props.trigger,
  (newTrigger, oldTrigger) => {
    if (newTrigger !== oldTrigger) {
      // clear the events
      events = {};
      outerEvents = {};
      attachEvents();
    }
  },
);
watch(
  isOpen,
  (newValue) => {
    if (newValue) {
      if (triggerNode.value && popperNode.value) {
        popperInstance = createPopper(triggerNode.value, popperNode.value, popperOptions.value);
      } else {
        popperInstance?.destroy();
      }
    }
  },
  { flush: 'post' },
);

onUnmounted(() => {
  popperInstance?.destroy();
});
defineExpose<TooltipInstance>({
  show: openFinal,
  hide: closeFinal,
});
</script>
<style lang="scss" scoped>
.ad-tooltip {
  --ad-popover-bg-color: #ffffff;
  --ad-popover-font-size: 14px;
  --ad-popover-border-color: #e4e7ed;
  --ad-popover-font-color: #000;
  --ad-popover-padding: 12px;
  --ad-popover-border-radius: 4px;
  display: inline-block;
}
.dark {
  .ad-tooltip {
    --ad-popover-bg-color: #1d1e1f;
    --ad-popover-border-color: #414243;
    --ad-popover-font-color: #fff;
  }
}
.ad-tooltip {
  .ad-tooltip__popper {
    background: var(--ad-popover-bg-color);
    border-radius: var(--ad-popover-border-radius);
    border: 1px solid var(--ad-popover-border-color);
    padding: var(--ad-popover-padding);
    color: var(-ad-popover-font-color);
    line-height: 1.4;
    text-align: justify;
    font-size: var(--ad-popover-font-size);
    word-break: break-all;
    box-sizing: border-box;
    z-index: 1000;
    #arrow,
    #arrow::before {
      position: absolute;
      width: 8px;
      height: 8px;
      box-sizing: border-box;
      background: var(--ad-popover-bg-color);
    }
    #arrow {
      visibility: hidden;
    }
    #arrow::before {
      visibility: visible;
      content: '';
      transform: rotate(45deg);
    }
    &[data-popper-placement^='top'] > #arrow {
      bottom: -5px;
    }

    &[data-popper-placement^='bottom'] > #arrow {
      top: -5px;
    }

    &[data-popper-placement^='left'] > #arrow {
      right: -5px;
    }

    &[data-popper-placement^='right'] > #arrow {
      left: -5px;
    }
    &[data-popper-placement^='top'] > #arrow::before {
      border-right: 1px solid var(--ad-popover-border-color);
      border-bottom: 1px solid var(--ad-popover-border-color);
    }
    &[data-popper-placement^='bottom'] > #arrow::before {
      border-left: 1px solid var(--ad-popover-border-color);
      border-top: 1px solid var(--ad-popover-border-color);
    }
    &[data-popper-placement^='left'] > #arrow::before {
      border-right: 1px solid var(--ad-popover-border-color);
      border-top: 1px solid var(--ad-popover-border-color);
    }
    &[data-popper-placement^='right'] > #arrow::before {
      border-left: 1px solid var(--ad-popover-border-color);
      border-bottom: 1px solid var(--ad-popover-border-color);
    }
  }

  .fade-enter-active,
  .fade-leave-active {
    transition: opacity 0.3s;
  }

  .fade-enter-from,
  .fade-leave-to {
    opacity: 0;
  }
}
</style>
